package ar.edu.uba.fi.jmit.aspecter;

import java.awt.geom.Point2D;
import java.util.LinkedList;
import java.util.List;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;
import ar.edu.uba.fi.jmit.aspecter.core.MethodDefinition;
import ar.edu.uba.fi.jmit.aspecter.core.exception.AspecterRuntimeException;
import ar.edu.uba.fi.jmit.aspecter.testUtils.Point;

public class MethodDefinitionTestCase extends AspecterTestCase {
	
	public void testToString() {
		MethodDefinition def = new MethodDefinition(Point.class, "getX", new LinkedList<Class<?>>(), int.class);
		assertEquals("int_ar.edu.uba.fi.jmit.aspecter.testUtils.Point.getX()", def.toString());

		List<Class<?>> paramsList = new LinkedList<Class<?>>();
		paramsList.add(int.class);
		def = new MethodDefinition(Point.class, "setX",paramsList, void.class);
		assertEquals("void_ar.edu.uba.fi.jmit.aspecter.testUtils.Point.setX(int)", def.toString());
		
		paramsList = new LinkedList<Class<?>>();
		paramsList.add(String.class);
		def = new MethodDefinition(Point.class, "someMethod",paramsList, String.class);
		assertEquals("java.lang.String_ar.edu.uba.fi.jmit.aspecter.testUtils.Point.someMethod(java.lang.String)", def.toString());
	}
	
	public void testConstructor() {
		try {
			new MethodDefinition(null, "getX", new LinkedList<Class<?>>(), int.class);
			fail("Exception should be thrown");
		}catch(AspecterRuntimeException e) {
			//expected
		}
		
		try {
			new MethodDefinition(Point.class, null, new LinkedList<Class<?>>(), int.class);
			fail("Exception should be thrown");
		}catch(AspecterRuntimeException e) {
			//expected
		}
		
		try {
			new MethodDefinition(Point.class, "getX", new LinkedList<Class<?>>(), null);
		}catch(AspecterRuntimeException e) {
			fail(e);
		}
		
		try {
			new MethodDefinition(Point.class, "getX", null, int.class);
		}catch(AspecterRuntimeException e) {
			fail(e);
		}
	}
	
	public void testEquals() {
		MethodDefinition def1 = new MethodDefinition(Point.class, "getX", new LinkedList<Class<?>>(), int.class);
		MethodDefinition def2 = new MethodDefinition(Point.class, "getX", new LinkedList<Class<?>>(), int.class);
		assertEquals(def1, def2);
		def2 = new MethodDefinition(Point.class, "getY", new LinkedList<Class<?>>(), int.class);
		assertFalse(def1.equals(def2));
		def2 = new MethodDefinition(Point.class, "getX", new LinkedList<Class<?>>(), Integer.class);
		assertFalse(def1.equals(def2));
		def2 = new MethodDefinition(Point.class, "getX", null, int.class);
		assertFalse(def1.equals(def2));
		def2 = new MethodDefinition(MethodDefinitionTestCase.class, "getX", new LinkedList<Class<?>>(), Integer.class);
		assertFalse(def1.equals(def2));
		def2 = new MethodDefinition(Point.class, "getX", new LinkedList<Class<?>>(), null);
		assertFalse(def1.equals(def2));
		
		List<Class<?>> paramsList = new LinkedList<Class<?>>();
		paramsList.add(int.class);
		paramsList.add(long.class);
		paramsList.add(String.class);
		def1 = new MethodDefinition(Point.class, "getX", paramsList, int.class);
		def2 = new MethodDefinition(Point.class, "getX", paramsList, int.class);
		assertEquals(def1, def2);
		
		paramsList = new LinkedList<Class<?>>();
		paramsList.add(int.class);
		paramsList.add(long.class);
		paramsList.add(String.class);
		def2 = new MethodDefinition(Point.class, "getX", paramsList, int.class);
		assertEquals(def1, def2);
		
		paramsList = new LinkedList<Class<?>>();
		paramsList.add(int.class);
		paramsList.add(long.class);
		def2 = new MethodDefinition(Point.class, "getX", paramsList, int.class);
		assertFalse(def1.equals(def2));
		
		paramsList = new LinkedList<Class<?>>();
		paramsList.add(int.class);
		paramsList.add(String.class);
		paramsList.add(long.class);
		def2 = new MethodDefinition(Point.class, "getX", paramsList, int.class);
		assertFalse(def1.equals(def2));
		
		def2 = new MethodDefinition(Point.class, "getX", null, int.class);
		assertFalse(def1.equals(def2));
	}
	
	public void testMatch() {
		try {
			MethodDefinition def1 = new MethodDefinition(Point.class, "getX", null, int.class);
			CtMethod ctMethod1 = CtNewMethod.make(CtClass.intType, "getX", new CtClass[]{}, new CtClass[]{}, "{return 0;}", ClassPool.getDefault().get(Point.class.getName()));
			assertTrue(def1.match(ctMethod1));
			CtMethod ctMethod2 = CtNewMethod.make(CtClass.intType, "getX", new CtClass[]{CtClass.intType, CtClass.longType}, new CtClass[]{}, "{return 0;}", ClassPool.getDefault().get(Point.class.getName()));
			assertTrue(def1.match(ctMethod2));
			
			MethodDefinition def2 = new MethodDefinition(Point.class, "getX", new LinkedList<Class<?>>(), int.class);
			assertTrue(def2.match(ctMethod1));
			assertFalse(def2.match(ctMethod2));
			
			MethodDefinition def3 = new MethodDefinition(Point.class, "getY", null, int.class);
			assertFalse(def3.match(ctMethod1));
			assertFalse(def3.match(ctMethod2));
			
			MethodDefinition def4 = new MethodDefinition(Point2D.class, "getX", null, int.class);
			assertFalse(def4.match(ctMethod1));
			assertFalse(def4.match(ctMethod2));
			
			MethodDefinition def5 = new MethodDefinition(Point.class, "getX", null, long.class);
			assertFalse(def5.match(ctMethod1));
			assertFalse(def5.match(ctMethod2));
			
			MethodDefinition def6 = new MethodDefinition(Point.class, "getX", null, null);
			assertTrue(def6.match(ctMethod1));
			assertTrue(def6.match(ctMethod2));
			
			MethodDefinition def7 = new MethodDefinition(Point.class, "getX", new LinkedList<Class<?>>(), null);
			assertTrue(def7.match(ctMethod1));
			assertFalse(def7.match(ctMethod2));
		} catch (Exception e) {
			fail(e);
		} 
	}
	
	public void testHashCode() {
		List<MethodDefinition> methodDefinitions = new LinkedList<MethodDefinition>();
		methodDefinitions.add(new MethodDefinition(Point.class, "getX", null, null));
		methodDefinitions.add(new MethodDefinition(Point.class, "getY", null, null));
		methodDefinitions.add(new MethodDefinition(Point.class, "setY", null, null));
		methodDefinitions.add(new MethodDefinition(Point.class, "setY", null, int.class));
		methodDefinitions.add(new MethodDefinition(Point.class, "setY", new LinkedList<Class<?>>(), null));
		methodDefinitions.add(new MethodDefinition(Point.class, "setY", new LinkedList<Class<?>>(), int.class));
		methodDefinitions.add(new MethodDefinition(Point2D.class, "setY", new LinkedList<Class<?>>(), null));
		methodDefinitions.add(new MethodDefinition(Point2D.class, "setY", null, long.class));
		List<Class<?>> params = new LinkedList<Class<?>>();
		params.add(long.class);
		params.add(Integer.class);
		params.add(Point.class);
		methodDefinitions.add(new MethodDefinition(Point.class, "", params, long.class));
		methodDefinitions.add(new MethodDefinition(Point.class, "", params, int.class));
		methodDefinitions.add(new MethodDefinition(Point.class, "", params, null));
		methodDefinitions.add(new MethodDefinition(Point.class, "doSomething", params, long.class));
		methodDefinitions.add(new MethodDefinition(Point2D.class, "", params, long.class));
		
		for(MethodDefinition definitionLevel1:methodDefinitions) {
			for(MethodDefinition definitionLevel2:methodDefinitions) {
				assertEquals(definitionLevel1.equals(definitionLevel2), definitionLevel1.hashCode() == definitionLevel2.hashCode());
			}
		}
	}
}

